/// <reference path="../model/Point.ts" />
/// <reference path="../model/BlockPoint.ts" />
/// <reference path="../model/Shape.ts" />

namespace tetris {
    import IPoint = model.IPoint;
    import Point = model.Point;
    import BlockPoint = model.BlockPoint;
    import Shape = model.Shape;

    function randomInt(): number {
        return ~~(1000 * Math.random());
    }

    function getRotatePosition(
        position: Point,
        form: model.Form,
        right: number,
        bottom: number): IPoint {
        return {
            x: Math.min(position.x, right - form.width),
            y: Math.min(position.y, bottom - form.height)
        };
    }

    export class Block {
        private _position: Point;
        private _shape: Shape;
        private _formIndex: number;

        constructor(shapeIndex: number, formIndex: number = randomInt()) {
            this._position = new Point(0, 0);
            this._shape = model.SHAPES[shapeIndex % model.SHAPES.length];
            this._formIndex = formIndex % this._shape.formsCount;
        }

        rotate(width: number = Infinity, height: number = Infinity): Block {
            this._formIndex = (this._formIndex + 1) % this._shape.formsCount;
            const p = getRotatePosition(this.position, this.form, width, height);
            return this.setPosition(p);
        }

        moveLeft(): Block {
            if (this.left > 0) {
                this._position = this._position.move(-1);
            }
            return this;
        }

        moveRight(width: number = Infinity): Block {
            if (this.right + 1 < width) {
                this._position = this._position.move(1);
            }
            return this;
        }

        moveDown(height: number = Infinity): Block {
            if (this.bottom + 1 < height) {
                this._position = this._position.move(0, 1);
            }
            return this;
        }

        setPosition(point: IPoint): Block;
        setPosition(x: number, y: number): Block;
        setPosition(x: any, y?: number): Block {
            this._position = y === void 0 ? new Point(x) : new Point(x, y);
            return this;
        }

        get form(): model.Form {
            return this._shape.getForm(this._formIndex);
        }

        get width(): number {
            return this.form.width;
        }

        get height(): number {
            return this.form.height;
        }

        get left(): number {
            return this.position.x;
        }

        get top(): number {
            return this.position.y;
        }

        get right(): number {
            return this.left + this.width - 1;
        }

        get bottom(): number {
            return this.top + this.height - 1;
        }

        get position(): Point {
            return this._position;
        }

        get color(): string {
            return this._shape.color;
        }

        fastenOffset(offsetX: number, offsetY: number): Array<BlockPoint> {
            return this.fasten(this.left + offsetX, this.top + offsetY);
        }

        fastenRotate(width: number = Infinity, height: number = Infinity): Array<BlockPoint> {
            const nextIndex = (this._formIndex + 1) % this._shape.formsCount;
            const nextForm = this._shape.getForm(nextIndex);
            const p = getRotatePosition(this.position, nextForm, width, height);
            return this.fasten(p.x, p.y, nextForm);
        }

        fasten(
            left: number = this.left,
            top: number = this.top,
            form: model.Form = this.form): Array<BlockPoint> {
            return form.points
                .map(point => new BlockPoint(left + point.x, top + point.y, this.color))
                .filter(t => t.x >= 0 && t.y >= 0);
        }
    }

    export class BlockFactory {
        private static _default: BlockFactory;

        static get default(): BlockFactory {
            return (this._default || (this._default = new BlockFactory));
        }

        create(): Block {
            return new Block(randomInt());
        }
    }
}